﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;

using LitJson;
using Pumpkin.Utils;

namespace Pumpkin.FrameBase.DownLoader
{
    public class ResVersion
    {
        public string Name;
        public string MD5;
        public int Size;
    }

    public class UpdateInfo
    {
        public List<ResVersion> UpdateList;
        public int TotalSize;
    }

    public class UpdateWorkflow : IModuleComponent
    {
        public static string LocalResVersionPath
        {
            get
            {
                return FileHelper.CombinePath(FileHelper.GetPersisitentPath(), HotFixConfigs.ResVersionFile);
            }
        }

        public static string LocalFileTablePath
        {
            get
            {
                return FileHelper.CombinePath(FileHelper.GetPersisitentPath(), HotFixConfigs.BundleFileTable);
            }
        }

        public static string LocalBundleDir
        {
            get
            {
                return FileHelper.CombinePath(FileHelper.GetPersisitentPath(), HotFixConfigs.BundleRootDir);
            }
        }

        public static string LocalExt2Name2Path
        {
            get
            {
                return FileHelper.CombinePath(FileHelper.GetPersisitentPath(), HotFixConfigs.Ext2File2PathFile);
            }
        }
        public int NewVersion = 0;

        private UpdateInfo updateInfo_ = new UpdateInfo();
        private string remoteVersionText;
        private string messageHead = "[UpdateWorkflow]: ";
        private IFrameModule module_;

        public UpdateWorkflow()
        {
        }

        public void Startup()
        {
            GetVersion(CheckUpdate);
        }

        public void SetModule(IFrameModule module)
        {
            module_ = module;
        }

        private void GetVersion(Action<int> action)
        {
            Log.LogInfo(messageHead + "Begin to check version");
            string versionUri = GlobalConfigs.Instance.GetVersionUri();
            CoroutineManager.DoCoroutine(DownLoadAsync(versionUri, request =>
            {
                NewVersion = int.Parse(request.downloadHandler.text);
                action(NewVersion);
            }));
        }

        private IEnumerator DownLoadAsync(string uri, Action<UnityWebRequest> action)
        {
            var request = UnityWebRequest.Get(uri);
            yield return request.SendWebRequest();
            action(request);
        }

        private void CheckUpdate(int newVersion)
        {
            var oldVersion = PlayerPrefs.GetInt(HotFixConfigs.KeyVersion, 0);
            if (newVersion == oldVersion)
            {
                Log.LogInfo(messageHead + "Version " + newVersion + " is Already latest version");
                module_.OnLoaded();
                return;
            }

            Log.LogInfo(messageHead + "Local version is [" + oldVersion + "] compare to the lastest version [" + newVersion + "]");
            Log.LogInfo(messageHead + "Game need update.");
            Log.LogInfo(messageHead + "Begin to download resource version.");
            string resVersionUri = GlobalConfigs.Instance.GetResVersionUri();
            CoroutineManager.DoCoroutine(DownLoadAsync(resVersionUri, request =>
            {
                remoteVersionText = request.downloadHandler.text;
                var remoteVersionData = JsonMapper.ToObject<List<ResVersion>>(remoteVersionText);
                string localVersionText = FileHelper.FileReadAllText(LocalResVersionPath);
                if (localVersionText == null)
                {
                    updateInfo_.UpdateList = remoteVersionData;
                    foreach (var item in updateInfo_.UpdateList)
                    {
                        updateInfo_.TotalSize += item.Size;
                    }
                }
                else
                {
                    Dictionary<string, ResVersion> localVersionDict = new Dictionary<string, ResVersion>();
                    List<ResVersion> localVersionData;
                    try
                    {
                        localVersionData = JsonMapper.ToObject<List<ResVersion>>(localVersionText);
                    }
                    catch (Exception e)
                    {
                        Log.LogWarning("Exception:" + e.Message +
                            "local resource version file broken, trying fix it");
                        FileHelper.DeleteFile(LocalResVersionPath);
                        localVersionData = new List<ResVersion>();
                    }

                    foreach (var localItem in localVersionData)
                    {
                        localVersionDict.Add(localItem.Name, localItem);
                    }

                    updateInfo_.UpdateList = new List<ResVersion>();
                    foreach (var item in remoteVersionData)
                    {
                        ResVersion resVersion;
                        localVersionDict.TryGetValue(item.Name, out resVersion);
                        if (resVersion == null || resVersion.MD5 != item.MD5)
                        {
                            updateInfo_.UpdateList.Add(item);
                            updateInfo_.TotalSize += item.Size;
                        }
                    }
                }

                if (module_.HasModuleEvent(DownloadModule.EventDownloadBeginNotify))
                {
                    module_.TriggerModuleEvent(DownloadModule.EventDownloadBeginNotify, updateInfo_.TotalSize, null);
                }
                else
                {
                    DoUpdate();
                }
            }));
        }

        public void DoUpdate()
        {
            CoroutineManager.DoCoroutine(UpdateBundles());
        }

        private IEnumerator UpdateBundles()
        {
            Log.LogInfo(messageHead + "Begin to download asset bundles :");

            //file2bundle
            var tableUri = GlobalConfigs.Instance.GetFileTableUri();
            var tableRequest = UnityWebRequest.Get(tableUri);
            yield return tableRequest.SendWebRequest();
            if (tableRequest.isNetworkError)
            {
                Log.LogError("An Error has ocurred while downloading bundle_table_file");
                yield break;
            }
            FileHelper.FileWriteAllText(LocalFileTablePath, tableRequest.downloadHandler.text, false);

            //ext2file2path
            if (HotFixConfigs.EnableResLoadByName)
            {
                var ext2file2path_uri = GlobalConfigs.Instance.GetExt2Name2PathUri();
                var ext2file2path_request = UnityWebRequest.Get(ext2file2path_uri);
                yield return ext2file2path_request.SendWebRequest();
                if (ext2file2path_request.isNetworkError)
                {
                    Log.LogError("An Error has ocurred while downloading ext2name2path");
                    yield break;
                }
                FileHelper.FileWriteAllText(LocalExt2Name2Path, ext2file2path_request.downloadHandler.text, false);
            }

            string localBundleDir = LocalBundleDir;
            FileHelper.CreateDirectory(localBundleDir);
            ulong downloadedBytes = 0;
            foreach (var item in updateInfo_.UpdateList)
            {
                Log.LogInfo(item.Name);
                var bundleUri = GlobalConfigs.Instance.GetBundleUri(item.Name);
                var request = UnityWebRequest.Get(bundleUri);
                request.SendWebRequest();
                while (!request.isDone)
                {
                    ulong size = downloadedBytes + request.downloadedBytes;
                    module_.TriggerModuleEvent(DownloadModule.EventDownloadProcessing, updateInfo_.TotalSize, (int)size);
                    yield return null;
                }

                if (request.isNetworkError)
                {
                    Log.LogError("An Error has ocurred while downloading bundle:" + item.Name);
                    yield break;
                }

                downloadedBytes += (ulong)item.Size;
                var localBundlePath = FileHelper.CombinePath(localBundleDir, item.Name);
                FileHelper.FileWriteAllBytes(localBundlePath, request.downloadHandler.data, false);
            }

            Log.LogInfo(messageHead + "Saving version datas.");
            FileHelper.FileWriteAllText(LocalResVersionPath, remoteVersionText, false);
            PlayerPrefs.SetInt(HotFixConfigs.KeyVersion, NewVersion);
            module_.OnLoaded();
        }
    }
}

